/*
 * Jenny Ruppen 09-730-391
 * Sebastien Goldberg 08-914-855
 * Henry Raymond 08-928-061
 * Stefan Mussato 09-752-270
 */
 
package ch.uzh.ifi.sopra.gruppe9.game;

import com.jme3.scene.Geometry;
import com.jme3.scene.Mesh;
import com.jme3.scene.Node;
import com.jme3.scene.VertexBuffer;


/**
 * Draws a text input field in the GUI.
 */
public class GuiTextInput extends Node implements IClickable, IUpdatable, IUnselectable {
	
	
	/**
	 * Value of the text input field.
	 */
	private String value;
	
	
	/**
	 * Value displayed in the text input field.
	 */
	private String displayedValue;
	
	
	/**
	 * Offset of the coordinate system of this text input field.
	 */
	private GUI.Offset offset;
	
	
	/**
	 * Position of the text input field in the x axis. (Assuming the window has a width of 1024 pixel.)
	 */
	private int positionX;
	
	
	/**
	 * Position of the text input field in the y axis. (Assuming the window has a height of 768 pixel.)
	 */
	private int positionY;
	
	
	/**
	 * Width of the text input field. (Assuming the window has a width of 1024 pixel.)
	 */
	private int width;
	
	
	/**
	 * Z-Index of the element. An element with a higher index will cover an element with a lower one.
	 */
	private int zIndex;
	
	
	/**
	 * Node of the text input field.
	 */
	private Node textInput;
	
	
	/**
	 * Text in the text input field.
	 */
	private GuiText text;
	
	
	/**
	 * True if the user can type into the field.
	 */
	private boolean active;
	
	
	/**
	 * Timer for cursor blinking. 
	 */
	private float timer;
	
	
	/**
	 * Cursor line.
	 */
	private Geometry cursor;
	
	
	/**
	 * Cursor position.
	 */
	private int cursorPosition = -1;
	
	
	/**
	 * Callback function which is called when the enter key is pressed.
	 */
	private ICallback action;
	
	
	/**
	 * Constructor
	 */
	public GuiTextInput(String value) {
		this.value = value;
		active = false;
		this.action = new ICallback() {
			public void run() {}
		};
	}
	
	
	/**
	 * Sets position and size
	 */
	public void setPositionSize(GUI.Offset offset, int positionX, int positionY, int width, int zIndex) {
		this.offset = offset;
		this.positionX = positionX;
		this.positionY = positionY;
		this.width = width;
		this.zIndex = zIndex;
	}
	
	
	/**
	 * Creates mesh and geometry and returns element;
	 */
	public GuiTextInput create() {		
		
		textInput = new Node();
		
		GuiQuad textInput1 = new GuiQuad();
		textInput1.setAbsolutePositionSize(-10, -10, 30, 60, 0);
		textInput1.setMaterial(MaterialManager.getTextInput(), 0.0f, 0.0f, 0.33333f, 1.0f);
		textInput.attachChild(textInput1.create());
		
		GuiQuad textInput2 = new GuiQuad();
		textInput2.setAbsolutePositionSize(20, -10, width - 40, 60, 0);
		textInput2.setMaterial(MaterialManager.getTextInput(), 0.33333f, 0.0f, 0.33333f, 1.0f);
		textInput.attachChild(textInput2.create());
		
		GuiQuad textInput3 = new GuiQuad();
		textInput3.setAbsolutePositionSize(width - 20, -10, 30, 60, 0);
		textInput3.setMaterial(MaterialManager.getTextInput(), 0.66666f, 0.0f, 0.33333f, 1.0f);
		textInput.attachChild(textInput3.create());
		
		textInput.setLocalTranslation(GUI.calculateX(offset, positionX), GUI.calculateY(offset, positionY), (float)zIndex);
     		
        this.attachChild(textInput);
        
        displayedValue = GuiText.getCroppedText(value, Fonts.getThin(), width - 11);
        
		text = new GuiText(displayedValue, Fonts.getThin());
		text.setPositionSize(GUI.Offset.WINDOW, 10, 778, 1);
		
		text.setLocalTranslation(GUI.calculateX(offset, positionX), GUI.calculateY(offset, positionY), zIndex + 0.5f);
        
		this.attachChild(text);
		
		Mesh cursorMesh = new Mesh();
		cursorMesh.setMode(Mesh.Mode.Lines);
		cursorMesh.setBuffer(VertexBuffer.Type.Position, 3, new float[] {0.0f, -8.0f * GUI.getPixelScale(), 0.0f, 0.0f, -32.0f * GUI.getPixelScale(), 0.0f});
		cursorMesh.setBuffer(VertexBuffer.Type.Index, 2, new short[] {0, 1});
		
		cursor = new Geometry("Cursor", cursorMesh);
		cursor.setMaterial(MaterialManager.getBlack());
		
		return this;
	}
	
	
	/**
	 * Set callback function which is called when the enter key is pressed.
	 */
	public void setAction(ICallback action) {
		this.action = action;
	}
	
	
	@Override
	public GUI.Offset getOffset() {
		return offset;
	}


	@Override
	public int getPositionX() {
		return positionX;
	}


	@Override
	public int getPositionY() {
		return positionY;
	}


	@Override
	public int getWidth() {
		return width;
	}


	@Override
	public int getHeight() {
		return 40;
	}


	@Override
	public void onMouseDown(int mouseX, int mouseY) {
		timer = 0.0f;
		if(!active) {
			TextInput.start(value);
			GUI.registerUpdatable(this);
			GUI.registerUnselectable(this);
			active = true;
		}
		int newCursorPosition = 0;
		int distanceToCursor = Math.abs(mouseX - 10);
		for(int i = 1; i <= value.length(); i++) {
			int newDistanceToCursor = Math.abs(mouseX - (GuiText.getTextWidth(value.substring(0, i), Fonts.getThin()) + 10));
			if(newDistanceToCursor > distanceToCursor) {
				break;
			}
			distanceToCursor = newDistanceToCursor;
			newCursorPosition++;
		}
		TextInput.setCursorPosition(newCursorPosition);
	}


	@Override
	public void onMouseUp(int mouseX, int mouseY) {
	}
	
	
	/**
	 * Process update cycle.
	 */
	@Override
	public void update(float delta) {
		
		timer += delta;
		while(timer > 1.2f) {
			timer -= 1.2f;
		}
		
		if(timer < 0.6f && !this.hasChild(cursor) && value.equals(displayedValue)) {
			this.attachChild(cursor);
		} else if((timer >= 0.6f || !value.equals(displayedValue)) && this.hasChild(cursor)) {
			this.detachChild(cursor);
		}
		
		if(!value.equals(TextInput.getString())) {
			timer = 0.0f;
			value = TextInput.getString();
			displayedValue = GuiText.getCroppedText(value, Fonts.getThin(), width - 11);
			text.setText(displayedValue);
		}
		
		if(cursorPosition != TextInput.getCursorPosition()) {
			timer = 0.0f;
			
			cursorPosition = TextInput.getCursorPosition();
		
			int cursorX = positionX + 12 + GuiText.getTextWidth(value.substring(0, cursorPosition), Fonts.getThin());
			if(cursorPosition >= 1 && value.substring(cursorPosition - 1, cursorPosition).equals(" ")) {
				cursorX += 5;
			}
			cursor.setLocalTranslation(GUI.calculateX(offset, cursorX), GUI.calculateY(offset, positionY), zIndex + 0.6f);
		}
		
		if(TextInput.hasEnterKeyBeenPressed()) {
			action.run();
		}
	}
	
	
	/**
	 * Stop all functions of text field
	 */
	public void stop() {
		if(active) {
			GUI.unregisterUnselectable(this);
			TextInput.end();
			GUI.unregisterUpdatable(this);
			active = false;
			if(this.hasChild(cursor)) {
				this.detachChild(cursor);
			}
		}
	}

	
	@Override
	public void onUnselect() {
		stop();
	}


	public String getValue() {
		return value;
	}
	
	
	public void setValue(String value) {
		this.value = value;
		if(active) {
			TextInput.setString(value);
		}
		displayedValue = GuiText.getCroppedText(value, Fonts.getThin(), width - 11);
		text.setText(displayedValue);
	}

}
